package drds.plus.executor.function;

import drds.plus.executor.ExecuteContext;
import drds.plus.executor.function.scalar.ScalarFunction;
import drds.plus.executor.row_values.RowValues;
import drds.plus.executor.utils.Utils;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.Item;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.column.Column;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.function.Function;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.function.FunctionType;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.function.extra_function.IExtraFunction;
import drds.plus.sql_process.type.Type;
import drds.plus.sql_process.type.Types;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public abstract class ExtraFunction implements IExtraFunction {

    protected Function function;

    public void setFunction(Function function) {
        this.function = function;
    }

    protected List getMapArgList(Function function) {
        return function.getArgList();
    }

    /**
     * 如果可以用db的函数，那就直接使用
     */
    protected abstract String getDataBaseFunction();

    protected List getReduceArgList() {
        String dbFunction = getDataBaseFunction();
        Object[] objects = dbFunction.split(",");
        return Arrays.asList(objects);
    }

    protected Object getArgValue(ExecuteContext executeContext, Object arg, RowValues rowData) {
        if (arg instanceof Function) {
            if (((Function) arg).getExtraFunction().getFunctionType().equals(FunctionType.aggregate_function)) {
                // aggregate_function function
                return Utils.getValue(rowData, ((Item) arg));
            } else {
                // scalar function
                return ((ScalarFunction) ((Function) arg).getExtraFunction()).scalarCalucate(executeContext, rowData);
            }
        } else if (arg instanceof Item) {// 如果是IColumn，那么应该从输入的参数中获取对应column
            if (Column.star.equals(((Item) arg).getColumnName())) {
                return rowData;
            } else {
                return Utils.getValue(rowData, ((Item) arg));
            }
        } else if (arg instanceof List) {
            List list = new ArrayList(((List) arg).size());
            for (Object object : (List) arg) {
                list.add(this.getArgValue(executeContext, object, rowData));
            }
            return list;
        } else {
            return arg;
        }
    }

    /**
     * 返回第一个类型的DataType
     */
    protected Type getFirstArgType() {
        return getType(function.getArgList().get(0));
    }

    /**
     * 返回对应类型的DataType
     */
    protected Type getType(Object arg) {
        Type type = null;
        if (arg instanceof Item) {
            type = ((Item) arg).getType();
        }
        if (type == null) {
            type = Types.getTypeOfObject(arg);
        }
        return type;
    }

    /**
     * 识别多个列，调整数字类型到相同level，针对出现非数字类型直接返回string
     *
     * <pre>
     * 1. 如果所有参数的类型相同，则返回相同的类型
     * 2. 否则均返回string类型
     *
     * 例子：
     * 1. query greast('1',2)，返回string
     * 2. query greast(1,2)，返回long
     * </pre>
     */
    protected Type getArgListTypeAndUseStringTypeWhenOccureNonNumberType() {
        // 遍历所有的then字段的类型
        List argList = function.getArgList();
        TypeLevel lastTypeLevel = null;
        for (int i = 0; i < argList.size(); i++) {
            Type argType = getType(argList.get(i));
            if (lastTypeLevel == null) {
                lastTypeLevel = getTypeLevel(argType);
                if (lastTypeLevel.isOther()) { // 出现非数字类型，直接返回string
                    lastTypeLevel.type = Type.StringType;
                    return lastTypeLevel.type;
                }
            } else if (argType != lastTypeLevel.type) {
                TypeLevel typeLevel = getTypeLevel(argType);
                if (typeLevel.isOther()) { // 出现非数字类型，直接返回string
                    typeLevel.type = Type.StringType;
                    return typeLevel.type;
                }
                if (typeLevel.level < lastTypeLevel.level) {
                    lastTypeLevel = typeLevel;
                }
            }
        }

        return lastTypeLevel.type;
    }

    /**
     * 识别多个列，调整数字类型到相同level，针对出现非数字类型按照long类型处理
     *
     * <pre>
     * 1. 如果所有参数的类型相同，则返回相同的类型
     * 2. 否则均返回string类型
     *
     * 例子：
     * 1. query 1+1.1，返回bigdecimal
     * 2. query 1+1，返回long
     * </pre>
     */
    protected Type getArgListTypeAndUseLongTypeWhenOccureNonNumberType() {
        List argList = function.getArgList();
        TypeLevel lastTypeLevel = null;

        for (int i = 0; i < argList.size(); i++) {
            Type argType = getType(argList.get(i));
            if (lastTypeLevel == null) {
                lastTypeLevel = getTypeLevel(argType);
                if (lastTypeLevel.isOther()) { // 强转成数字类型
                    lastTypeLevel.level = 3;
                    lastTypeLevel.type = Type.LongType;
                }
            } else if (argType != lastTypeLevel.type) {
                TypeLevel typeLevel = getTypeLevel(argType);
                if (typeLevel.isOther()) { // 强转成数字类型
                    typeLevel.level = 3;
                    typeLevel.type = Type.LongType;
                }
                if (typeLevel.level < lastTypeLevel.level) {
                    lastTypeLevel = typeLevel;
                }
            }
        }
        return lastTypeLevel.type;
    }

    /**
     * 识别多个列，调整数字类型到相同level，针对出现非数字类型采用firstArgType模式
     *
     * <pre>
     * 例子：
     * 1. query 1+1.1，返回bigdecimal
     * 2. query 1+1，返回long
     * </pre>
     */
    protected Type getArgListTypeAndUseFirstArgTypeWhenOccureNonNumberType() {
        List argList = function.getArgList();
        TypeLevel lastTypeLevel = null;

        for (int i = 0; i < argList.size(); i++) {
            Type type = getType(argList.get(i));
            if (lastTypeLevel == null) {//第一次出现
                lastTypeLevel = getTypeLevel(type);
                if (lastTypeLevel.isOther()) { // 出现非数字类型，直接返回
                    return lastTypeLevel.type;
                }
            } else if (type != lastTypeLevel.type) {
                TypeLevel typeLevel = getTypeLevel(type);
                if (typeLevel.isOther()) { // 出现非数字类型，直接返回
                    return lastTypeLevel.type;
                }
                if (typeLevel.level < lastTypeLevel.level) {//取最小
                    lastTypeLevel = typeLevel;
                }
            }
        }

        return lastTypeLevel.type;
    }

    /**
     * 类型越大,level越小
     */
    protected TypeLevel getTypeLevel(Type type) {
        TypeLevel typeLevel = new TypeLevel();
        if (type == Type.FloatType || type == Type.DoubleType || type == Type.BigDecimalType) {
            typeLevel.level = 1;
            typeLevel.type = Type.BigDecimalType;
        } else if (type == Type.BigIntegerType) {
            typeLevel.level = 2;
            typeLevel.type = Type.BigIntegerType;
        } else if (type == Type.LongType || type == Type.IntegerType || type == Type.ShortType || type == Type.BooleanType) {
            typeLevel.level = 3;
            typeLevel.type = Type.LongType;
        } else {
            typeLevel.level = TypeLevel.OTHER;
            typeLevel.type = type;
        }
        return typeLevel;
    }

}
